package main

import (
	"bytes"
	"context"
	"crypto"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha256"
	"crypto/x509"
	"encoding/base64"
	"encoding/json"
	"encoding/pem"
	"errors"
	firebase "firebase.google.com/go"
	"firebase.google.com/go/messaging"
	"fmt"
	gofcm "github.com/appleboy/go-fcm"
	jsoniter "github.com/json-iterator/go"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
	"gp_notify/common"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"time"
)

const firebaseScope = "https://www.googleapis.com/auth/firebase.messaging"

type tokenProvider struct {
	tokenSource oauth2.TokenSource
}

// newTokenProvider function to get token for fcm-send
func newTokenProvider(credentialsLocation string) (*tokenProvider, error) {
	jsonKey, err := ioutil.ReadFile(credentialsLocation)
	if err != nil {
		return nil, errors.New("fcm: failed to read credentials file at: " + credentialsLocation)
	}
	cfg, err := google.JWTConfigFromJSON(jsonKey, firebaseScope)
	if err != nil {
		return nil, errors.New("fcm: failed to get JWT config for the firebase.messaging scope")
	}
	ts := cfg.TokenSource(context.Background())
	return &tokenProvider{
		tokenSource: ts,
	}, nil
}

// credentialsFile is the unmarshalled representation of a credentials file.
type CredentialsFile struct {
	Type string `json:"type"`

	// Service Account fields
	ClientEmail    string `json:"client_email"`
	PrivateKeyID   string `json:"private_key_id"`
	PrivateKey     string `json:"private_key"`
	AuthURL        string `json:"auth_uri"`
	TokenURL       string `json:"token_uri"`
	ProjectID      string `json:"project_id"`
	UniverseDomain string `json:"universe_domain"`

	// User Credential fields
	// (These typically come from gcloud auth.)
	ClientSecret string `json:"client_secret"`
	ClientID     string `json:"client_id"`
}

func (src *tokenProvider) token() (string, error) {
	token, err := src.tokenSource.Token()
	if err != nil {
		return "", errors.New("fcm: failed to generate Bearer token")
	}
	return token.AccessToken, nil
}
func Token() (string, error) {
	//jsonKey, err := ioutil.ReadFile("./Config/watcherofrealms-ap-firebase-adminsdk-okhlk-cf21540ad6.json")
	jsonKey, err := ioutil.ReadFile("./Config/watcherofrealms-a9706-firebase-adminsdk-v8ih3-1a24b83ae9.json")
	if err != nil {
		panic(fmt.Errorf("fcm: failed to read credentials file at: "))
	}
	var cret CredentialsFile
	if err := json.Unmarshal(jsonKey, &cret); err != nil {
		panic(fmt.Errorf("fcm: failed to unmarshal credentials file"))
	}
	p, _ := pem.Decode([]byte(cret.PrivateKey))

	privKey, err := x509.ParsePKCS8PrivateKey(p.Bytes)
	if err != nil {
		panic(fmt.Errorf("GP ServiceAccount私钥x509解析错误 %s", err))
	}
	jwtHeader := base64.StdEncoding.EncodeToString([]byte(`{"alg":"RS256","typ":"JWT"}`))
	fmt.Printf("jwtHeader: %s\n", jwtHeader)

	// Reverting time back for machines whose time is not perfectly in sync.
	// If client machine's time is in the future according
	// to Google servers, an access token will not be issued.
	now := time.Now().Add(-10 * time.Second)
	claimSet := struct {
		Iss   string `json:"iss"`
		Scope string `json:"scope"`
		Aud   string `json:"aud"`
		Exp   int64  `json:"exp"`
		Iat   int64  `json:"iat"`
	}{
		Iss:   cret.ClientEmail,
		Scope: firebaseScope,
		Aud:   cret.TokenURL,
		Exp:   now.Add(time.Hour).Unix(),
		Iat:   now.Unix(),
	}

	jwtClaimSet, _ := jsoniter.MarshalToString(claimSet)
	//jwtClaimSet = "{\"iss\":\"firebase-adminsdk-okhlk@watcherofrealms-ap.iam.gserviceaccount.com\",\"scope\":\"https://www.googleapis.com/auth/firebase.messaging\",\"aud\":\"https://oauth2.googleapis.com/token\",\"exp\":1711984497,\"iat\":1711980897}"
	fmt.Printf("jwtClaimSet: %s\n", jwtClaimSet)
	jwtClaimSet = base64.StdEncoding.EncodeToString([]byte(jwtClaimSet))

	hashed := sha256.Sum256([]byte(jwtHeader + "." + jwtClaimSet))
	fmt.Printf("hashed：%+v jwtHeader:%+v jwtset:%+v\n", string(hashed[:]), jwtHeader, jwtClaimSet)
	signature, err := rsa.SignPKCS1v15(rand.Reader, privKey.(*rsa.PrivateKey), crypto.SHA256, hashed[:])
	if err != nil {
		panic(fmt.Errorf("GP ServiceAccount私钥签名错误 %s", err))
	}
	fmt.Printf("signature:%s \n %s\n", string(signature), base64.StdEncoding.EncodeToString(signature))
	assertion := jwtHeader + "." + jwtClaimSet + "." + base64.StdEncoding.EncodeToString(signature)
	fmt.Printf("-----------------------------assertion: %s\n", assertion)
	v := url.Values{}
	v.Add("grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer")
	v.Add("assertion", assertion)
	//resp, err := http.Post("http://oauth2.googleapis.com:5379/token", "application/x-www-form-urlencoded", bytes.NewBufferString(v.Encode()))
	resp, err := http.Post(cret.TokenURL, "application/x-www-form-urlencoded", bytes.NewBufferString(v.Encode()))
	if err != nil {
		panic(fmt.Errorf("GP ServiceAccount获取token错误 %s", err))
	}

	defer resp.Body.Close()
	respData, err := ioutil.ReadAll(resp.Body)
	fmt.Printf("rsp Data：%s error:%+v", string(respData), err)

	var respJson struct {
		AccessToken string `json:"access_token"`
		Scope       string `json:"scope"`
		TokenType   string `json:"token_type"`
		ExpiresIn   int    `json:"expires_in"`
	}
	err = jsoniter.Unmarshal(respData, &respJson)
	if err != nil {
		return "", err
	}

	return respJson.AccessToken, nil
	//ExpiresAt: time.Now().Add(time.Duration(respJson.ExpiresIn) * time.Second),
}

func OldPush() {
	msg := &gofcm.Message{
		Data: map[string]interface{}{
			"linkurl": "https://www.baidu.com/",
			"taskid":  "850",
		},
		Notification: &gofcm.Notification{
			Title: "title",
			Body:  "hello world!!!",
		},
		RegistrationIDs: []string{"ecKFJZt7QTOB52VCYTyXIo:APA91bHDmCJ18sQ89VuSyPYPobi37MA_n0IFS5kL4sf6_M126y8eJC2Eb5U04ze-ChLbkcaWZLuSkl8gBhRgYBawALc3lnhsX6VgkpYJiFHn101ykgA916YnWPDA9OtbwvPxJ2-6aq8B"},
	}

	// Create a FCM client to send the message.
	client, err := gofcm.NewClient("AAAAfFSbLvo:APA91bEDso4ch7PkCyScLhDTKfiuazkIbZd5LI91hLZxbikMGhrNwvpSHOBs6dBmkTdahbwPxfcvHZudAe2rv3fKlSpSXJHg9II1XonWlGEhPMw0leOjDPOR76VRMPe3qNYbOzSklYIC")
	if err != nil {
		panic(fmt.Errorf("newClient error:%+v", err))
	}

	// Send the message and receive the response without retries.
	response, err := client.Send(msg)
	if err != nil {
		panic(fmt.Errorf("send error:%+v", err))
	}

	fmt.Printf("oldPush %#v\n", response)
}

// --------------------main.go--------------------

func main() {
	//tp, err := newTokenProvider("./Config/fmt-ap-firebase-adminsdk-okhlk-cf21540ad6.json")
	//if err != nil {
	//	panic(fmt.Errorf("newTokenProvider error:%+v", err))
	//}
	//token, err := tp.token()
	//OldPush()
	token, err := Token()
	if err != nil {
		panic(fmt.Errorf("token error:%+v", err))
	}
	fmt.Println("-----token:", token)

	//resource := fmt.Sprintf("https://fcm.googleapis.com/v1/projects/watcherofrealms-ap/messages:send")
	resource := fmt.Sprintf("https://fcm.googleapis.com/v1/projects/watcherofrealms-a9706/messages:send")

	notification := &Notification{
		Title: "$GOOG up 1.43% on the day",
		Body:  "$GOOG gained 11.80 points to close at 835.67, up 1.43% on the day.",
	}
	message := &Message{
		Data: map[string]string{
			"taskid":  "850",
			"linkurl": "https://www.baidu.com/",
		},
		Notification: notification,
		//Android: &AndroidConfig{
		//	//Notification: &AndroidNotification{
		//	//	Title: "Android Title",
		//	//	Body:  "Android Body",
		//	//},
		//	Data: map[string]string{
		//		"taskid":  "850",
		//		"linkurl": "https://www.baidu.com/",
		//	},
		//},
		//Token: "cYngRKItRU6BLhdrFeGkcO:APA91bE7sured-zl1qtNw8XdjmA1FQME37vSGDjrJ4SETnoAW4hNXjZQbNJaIetJ9AaAq7LiCj4vjhNVFIDn8jKD7ZubXprpaPZMpM0fUCdVj_BHlKdm1ufqz3LHc07bCVdw0GtROiIE",
		//Token: "dHh5h2mRRta6bPv2fYw25q:APA91bGYbTj3ccUl6AKsAIfiIpeShKII0uAApaxVa8SwP8n9zslm4ZTqKkAuv57E_VBXkMN3FBELeCiIWmz2brNXYkVG_1hqS8CPFfMM3wV8YJ1XxP8kNS4jodClPB_aIpqyRcK_NSAz",
		Token: "ecKFJZt7QTOB52VCYTyXIo:APA91bHDmCJ18sQ89VuSyPYPobi37MA_n0IFS5kL4sf6_M126y8eJC2Eb5U04ze-ChLbkcaWZLuSkl8gBhRgYBawALc3lnhsX6VgkpYJiFHn101ykgA916YnWPDA9OtbwvPxJ2-6aq8B",
	}
	req := FcmRequest{
		Message: message,
	}
	data, err := jsoniter.Marshal(req)
	helper := common.HttpHelper{
		Header: http.Header{
			"Authorization": []string{fmt.Sprintf("Bearer %s", token)},
			"Content-Type":  []string{"application/json"},
		},
		ApiUrl: resource,
		Method: "POST",
		Body:   &data,
	}
	fmt.Printf("helper:%+v\n----------%+v\n", helper, string(data))
	rspData, err := helper.Do()
	//if err != nil {
	//	return
	//}
	fmt.Printf("rspData:%s error:%+v\n", string(rspData), err)
	fmt.Printf("----------------------\n")

	// Send a message to the device corresponding to the provided
	// registration token.
	//response, err := client.Send(ctx, message)
}

//----------------------------others--------------------------------------------------------------------------------------------

func sendMsgToTopic(ctx context.Context, client *messaging.Client, topic string) {
	notification := &messaging.Notification{
		Title: "0007 2019-04-23",
		Body:  "fffffffffffffffffffffffffff.",
	}

	// See documentation on defining a message payload.
	message := &messaging.Message{
		Data: map[string]string{
			"score": "88888",
			"time":  "2:45",
		},
		Notification: notification,
		Topic:        topic,
	}

	// Send a message to the devices subscribed to the provided topic.
	response, err := client.Send(ctx, message)
	if err != nil {
		log.Fatalln(err)
	}
	// Response is a message ID string.
	fmt.Println("Successfully sent message:", response)
}

func sendMsgToToken(ctx context.Context, client *messaging.Client, registrationToken string) {
	// See documentation on defining a message payload.

	notification := &messaging.Notification{
		Title: "$GOOG up 1.43% on the day",
		Body:  "$GOOG gained 11.80 points to close at 835.67, up 1.43% on the day.",
	}

	// timestampMillis := int64(12345)

	message := &messaging.Message{
		// Data: map[string]string{
		//  "score": "850",
		//  "time": "2:45",
		// },
		Notification: notification,
		Webpush: &messaging.WebpushConfig{
			Notification: &messaging.WebpushNotification{
				Title: "title",
				Body:  "body",
				//      Icon: "icon",
			},
			FcmOptions: &messaging.WebpushFcmOptions{
				Link: "https://fcm.googleapis.com/",
			},
		},
		Token: registrationToken,
	}

	// Send a message to the device corresponding to the provided
	// registration token.
	response, err := client.Send(ctx, message)
	if err != nil {
		log.Fatalln(err)
	}
	// Response is a message ID string.
	fmt.Println("Successfully sent message:", response)
}

func subscribe(ctx context.Context, client *messaging.Client, topic string, registrationTokens []string) {
	// Subscribe the devices corresponding to the registration tokens to the
	// topic.
	response, err := client.SubscribeToTopic(ctx, registrationTokens, topic)
	if err != nil {
		log.Fatalln(err)
	}
	// See the TopicManagementResponse reference documentation
	// for the contents of response.
	fmt.Println(response.SuccessCount, "tokens were subscribed successfully")
}

func createCustomToken(ctx context.Context, app *firebase.App) {
	authClient, err := app.Auth(context.Background())
	if err != nil {
		log.Fatalf("error getting Auth client: %v\n", err)
	}

	token, err := authClient.CustomToken(ctx, "25696773511053390")
	if err != nil {
		log.Fatalf("error minting custom token: %v\n", err)
	}

	log.Printf("Got custom token: %v\n", token)
}

//func main() {
//	var serviceAccountKey = []byte(`{
//        "type": "service_account",
//        "project_id": "lvxiaorun-22c97",
//        "private_key_id": "a212612afee74a54e9e30cb2bfbc5b0c118ca3cc",
//        "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCpieVk85eBv9Eh\nGktoEf7rv+lz7PQh6CTa4F2QtljaFM3k3hv4v3F+JYnVhWKYL1yz7D4vNgByv3lD\nsXGRMwe1uK5pP5DLke7X1q0RQJn928QrUKDK71MKqDPTeYMjZEoVLdFRbVzbvpf9\nwks6H+vh9CFGwJ4D/H/7tX//bRR/f6ape5J+VwEjWH0itLXd35klh3oqWCA4/Dwg\nxNg7LzejVdM+GOhyuvLCZfEOcno8wF+YI8Uj7Ut27Wj9fLcO2cW2ybaK8Y87CfKO\nKmQgSt1lqgbrksi+fe725BxbXZp/eF9MrS8CPLOWgv/DRW3aP0ocgxx6TX0z8K1a\ncFdvXwcxAgMBAAECggEABgfe9liY6NdlLceE66qKNiIhQIurAoLCvttw0Js/7WAE\nk/HXrmFG/P0CWmtQfsfehRLwAldqLCrF+kO3XboiOdNcNue5M5iZFanwBZdV8vsM\njxri4V0ih9REZa8inFFutjKnSb15amKs/uyYpvRoPGUmAuGKrWsftVk3OKONcVyP\nA2L8N/keTc8IWKe4GlIdfoU2f/hAxbVYiu0u2HXgdGlFmY1vpOrFgHbJZcXyUlhg\nQvtgCnt/bwKr3GkuWXJff9pm4wf7A2e73jv68zuucCV5P9iExVIAZIkLvhlxgqn1\ncKF7Ib5K/RVzdXgcVNpL0V6ewv+IC1fjHKLJYbXiDQKBgQDSbOXnxzb639NOIlvJ\nSb0wpTJVmWLWsIvJjO74K2ZNOQ38f7LDuvZ1/M2W0QsvVOw43Gpqijza0HV0aCRH\nqrPldgTRBJAqY/VexWt/kuOvrCA1IkXRl8jayLoMrGxCv12fiIp8ryDbikCuVDCE\n9VuenZGbe1++O5aBjMG/3c6sxQKBgQDOQgVAiAiTmaiUv9NPIX5sfpuV0U7QB6TT\n83wS45ahheanWf/G2CvidazK9KR5oEquljVGTPsmK01x17/1pnM+o6p84ZETNQ12\n4SsJpT7NxId/Vkw2Hmn/qzhHZa1wckdEPrHc70Upre/YJ6snp+brCBQNiuhALZde\nc6yrHZKvfQKBgQCNvlE3ydfdMjxyW26crpFEXWMEiigsGgxvngGzJfjpd89WEObo\nNd6jJ8GNIA96uKfOvZrpXWkUtGsKGMSnifNYVCF2cq5x/5dfWXjKHLZGtZmUcRu6\nzZW82o2Iz/S1GZcFScKPrqBhgkWDqK5uQaCPvfBBXd/mktkVNy2kAtOfSQKBgQC+\nx6xp+ynLtOaM6D4BRJ7Wpektk5QNsfRRJDdQlXi/4MXvZ7zBZTR6XJQ+ijkUUyKh\nCEkwxIXN0WHp+kERbCvO9b39kvsIxBq3KiEP4+wKkk0uiFkn+cvb87izuaXKi7nF\nsyP7ksnrenqN+mtC2/goz6kUubaHnmQTtnUxNcJ3VQKBgQDDbVOQx1LCex9HHAV4\n2Rula9E27w0ujYdhf3YvQ66mOG6Ig5+1YhiXrKrElfCCAVc7H8KmAt7iPHcJXKgC\nMJSJK7XWasj+Ld2SdbPT2LgwGglx907N+wyS47+vOh9Ppu3d5Tv9/BIKkvNKXBu+\ndQBw8Xw1P+U5QtIPf7LOKMtNEw==\n-----END PRIVATE KEY-----\n",
//        "client_email": "firebase-adminsdk-k3958@lvxiaorun-22c97.iam.gserviceaccount.com",
//        "client_id": "103134511880014813376",
//        "auth_uri": "https://accounts.google.com/o/oauth2/auth",
//        "token_uri": "https://oauth2.googleapis.com/token",
//        "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
//        "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-k3958%40lvxiaorun-22c97.iam.gserviceaccount.com"
//     }`)
//
//	// opt := option.WithCredentialsFile("path/to/serviceAccountKey.json")
//	opt := option.WithCredentialsJSON(serviceAccountKey)
//	app, err := firebase.NewApp(context.Background(), nil, opt)
//	if err != nil {
//		log.Fatalf("error initializing app: %v\n", err)
//	}
//
//	// Obtain a messaging.Client from the App.
//	ctx := context.Background()
//	client, err := app.Messaging(ctx)
//	if err != nil {
//		log.Fatalf("error getting Messaging client: %v\n", err)
//	}
//
//	uid := "25696773511053390"
//	// This registration token comes from the client FCM SDKs.
//	registrationToken := "fxV6ToLJh3A:APA91bGfcaOl4mmnj_mPY7MTscjzT0aZLvyK5xaLLboWavxFoeqc3hZu_npEtaINebzHAfOrARg4kn9RmWC9ZYKvhqJrPhnNI43qtUruQsvd7Or7w_ZnDG4agOMM_7xB0J4ci9UHPT5S"
//
//	// These registration tokens come from the client FCM SDKs.
//	registrationTokens := []string{
//		registrationToken,
//		// ...
//		"cBYSJNhfG_Q:APA91bFzLxiSVynUc2thc6aGfF1ba_6WoJvOctw2_1cIlUEr2r7Pf-n_Qk6uisLpc9Whcf-UU4WwcjnRwLTm_Zok1pH2RGw2_WvLmaT_AdZp84caH29haB4gQFIdrc0wQSr-vVgR0F3o",
//	}
//
//	subscribe(ctx, client, uid, registrationTokens)
//	// sendMsgToToken(ctx, client, registrationToken)
//	sendMsgToTopic(ctx, client, uid)
//	// createCustomToken(ctx, app)
//}
